<?php
/**
 *  Abstract class providing some more advanced type management.
 *
 *  Implements several virtual methods suitable for scalar types
 *
 */
abstract class Yz_Type
    extends Yz_Class
{

    /**
     *  expects : message texts for type expectations in test()
     *   @var array
     */
    protected static $expects = array();


    /**
     *  extract the raw (native PHP-type) value from $value
     *
     *  This method is virtual; it should be abstract, but PHP emits a warning
     *   @param mixed $value
     *   @return null for the Yz_Type class, otherwise mixed
     */
    public static function valueOf( $value )
    {
        return null;
    }


    /** Test if a value is usable as native-type and return it as native-type or throw exception
    *   @param mixed $value the value to test
    *   @param string(null) context
    *   @return mixed php native type
    *   @throws E_TypeException
    */
    public static function test( $value, $context=null )
    {
        return static::doTest( $value, $context, 'test', null );
    }

    /**
     *  execute the test function, raising InvalidArgument exception if its result is not bool true
    *   @param mixed value to test
    *   @param string(null) context
    *   @param string method name from which the doTest() was called
    *   @param callback: bool callback(typed value) - can always expect the value to be of the expected type
    *   @throw InvalidArgument
    */
    protected static function doTest( $value, $context, $method, $function )
    {
        $rvalue = static::valueOf( $value );
        if ( null === $rvalue ) {
            throw Yz_Exception_InvalidValueType::_new(array(
                'type' => gettype($value),
                'arg' => $context ?: get_called_class() . "::{$method}()",
                'expect'=> static::$expects[$method],
            ));
        }
        if ( null !== $function && true !== $function($rvalue) ) {
            throw Yz_Exception_InvalidArgument::_new(array(
                'value' => gettype($value),#'"' . substr($value, 0, 16) . '..."',
                'arg'  => $context ?: get_called_class() . "::{$method}()",
                'expect'=> static::$expects[$method],
            ));
        }
        return $rvalue;
    }

} // class

